package com.github.cakin.shiro.chapter15.credentials;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @className: RetryLimitHashedCredentialsMatcher
 * @description: 密码重试次数限制
 * @date: 2020/5/19
 * @author: cakin
 */
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
    /**
     * 声明缓存
     */
    private Cache<String, AtomicInteger> passwordRetryCache;

    public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
        // 获得一个缓存对象
        passwordRetryCache = cacheManager.getCache("passwordRetryCache");
    }
    /**
     * 功能描述：实现密码验证功能逻辑
     *
     * @param token 用户传入token
     * @return info 系统存储的认证信息
     * @author cakin
     * @date 2020/5/19
     */
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        // 获得身份信息
        String username = (String)token.getPrincipal();
        // 获取缓存对象的值
        AtomicInteger retryCount = passwordRetryCache.get(username);
        if(retryCount == null) {
            retryCount = new AtomicInteger(0);
            passwordRetryCache.put(username, retryCount);
        }
        // 次数超过5次，抛出异常，incrementAndGet 负责加1计数和取出
        if(retryCount.incrementAndGet() > 5) {
            throw new ExcessiveAttemptsException();
        }
        // 小于5次，才允许进行密码验证
        // 认证方法：使用盐加密传入的明文密码和此处的密文密码进行匹配。具体参考：doCredentialsMatch 实现
        boolean matches = super.doCredentialsMatch(token, info);
        // 密码验证成功，清缓存的数据，并返回匹配成功
        if(matches) {
            //clear retry count
            passwordRetryCache.remove(username);
        }
        return matches;
    }
}
